admin / Synapse-Sonar
publicAttack Surface Simulation
Synapse-Sonar / synapse-sonar / app / api / assets / [id] / blast-radius / route.ts
1286 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import { NextRequest, NextResponse } from "next/server"; import { requireTenantContext, HttpError } from "@/lib/auth"; import { assertCan } from "@/lib/rbac"; import { computeBlastRadius } from "@/lib/blastRadius"; export const runtime = "nodejs"; /** * GET /api/assets/:id/blast-radius?hops=2 * * Returns downstream assets reachable from the (assumed compromised) node * within `hops`, plus the edge IDs forming the lateral path. Tenant-scoped and * gated behind the graph:simulate permission (Analyst+). */ export async function GET( req: NextRequest, { params }: { params: { id: string } } ) { try { const ctx = await requireTenantContext(); assertCan(ctx.role, "graph:simulate"); const hopsParam = req.nextUrl.searchParams.get("hops"); const hops = Math.min(Math.max(parseInt(hopsParam ?? "2", 10) || 2, 1), 5); const result = await computeBlastRadius(ctx.tenantId, params.id, hops); return NextResponse.json(result); } catch (err) { if (err instanceof HttpError) { return NextResponse.json({ error: err.message }, { status: err.status }); } const status = (err as { status?: number }).status ?? 500; return NextResponse.json( { error: (err as Error).message ?? "Internal error" }, { status } ); } } |